package jmine.tec.batch.graph;

import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import jmine.tec.batch.JmineTecBatchMessages;
import jmine.tec.batch.PhaseRuntimeException;
import jmine.tec.batch.domain.MPhase;

import org.jgrapht.DirectedGraph;
import org.jgrapht.alg.CycleDetector;
import org.jgrapht.graph.DefaultDirectedGraph;
import org.jgrapht.graph.DefaultEdge;
import org.jgrapht.traverse.TopologicalOrderIterator;

/**
 * Representa um grafo de fases
 * 
 * @author jrenaut
 */
public class PhaseGraph {

    private DirectedGraph<MPhase, DefaultEdge> dgraph = new DefaultDirectedGraph<MPhase, DefaultEdge>(DefaultEdge.class);

    /**
     * @param phase uma definição de fase
     * @return self
     */
    public PhaseGraph add(MPhase phase) {
        if (!this.dgraph.containsVertex(phase)) {
            this.dgraph.addVertex(phase);
            Set<MPhase> deps = phase.getDependencies();
            for (MPhase dep : deps) {
                this.add(dep);
                this.dgraph.addEdge(dep, phase);
            }
        }
        return this;
    }

    /**
     * @param phases uma coleção de definições de fases
     * @return self
     */
    public PhaseGraph addAll(Collection<MPhase> phases) {
        for (MPhase phase : phases) {
            this.add(phase);
        }
        return this;
    }

    /**
     * @return a lista de fases que devem ser executadas
     */
    public List<MPhase> getOrderedPhases() {
        CycleDetector<MPhase, DefaultEdge> cdetector = new CycleDetector<MPhase, DefaultEdge>(this.dgraph);
        if (cdetector.detectCycles()) {
            throw new PhaseRuntimeException(JmineTecBatchMessages.JMINE_TEC_PHASE_CIRCULARITY_DETECTED.create(Arrays.toString(cdetector
                    .findCycles().toArray())));
        }

        List<MPhase> ret = new LinkedList<MPhase>();
        if (this.dgraph.vertexSet().size() > 0) {
            TopologicalOrderIterator<MPhase, DefaultEdge> topolog = new TopologicalOrderIterator<MPhase, DefaultEdge>(this.dgraph);
            while (topolog.hasNext()) {
                ret.add(topolog.next());
            }
        }

        return ret;
    }
}
